home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 / Ham Radio 2000.iso / ham2000 / packet / pktmon12 / pktmon12.pas < prev   
Pascal/Delphi Source File  |  1992-06-23  |  31KB  |  1,062 lines

  1. (*
  2.  
  3.    Packet Radio Monitor version 1.2
  4.    author: Pawel Jalocha
  5.            Rynek Kleparski 14/4a
  6.            PL-31150 Krakow, Poland
  7.    e-mail: jalocha@chopin.ifj.edu.pl
  8.            jalocha@priam.cern.ch
  9.            jalocha@vxcern.cern.ch
  10.  
  11.    This program may be freely used/copied/modified for non-commercial use.
  12.  
  13.    This program decodes HF and VHF packets.
  14.    It uses HamComm (or similar) interface.
  15.  
  16.    The audio signal from a receiver in connected to one of the
  17.    COM ports (DSR line) via 'Ham Comm' style interface which 'squares'
  18.    audio signal by mean of a simple comparator. Comparator output
  19.    steers RS232 DSR input.
  20.  
  21.    Each transition on DSR makes an interrupt. Interrupt service routine
  22.    reads the system timer (8253) so to find out what time elapsed
  23.    since previous transition. This way the program keeps track of
  24.    the audio signal period, frequency and timing.
  25.  
  26.    Ones you have frequency it is possible to decode bits from it,
  27.    find out X25 starting flag, build complete frames, etc...
  28.  
  29.    In HF mode the program is "hardwired" to 700 Hz center frequency.
  30.    It is intended to be used with 500 Hz CW filter. Precise
  31.    (better than 50 Hz) tuning is required
  32.  
  33.    In VHF mode it accepts FSK centered at 1700 Hz with deviation
  34.    either 800 Hz or 1000 Hz.
  35.  
  36.    This program was written and compiled with Turbo Pascal 6.0 and tested
  37.    on a 386SX/20MHz machine. I used COM2 port because mouse is sitting on
  38.    my COM1. I never actually tried whether it works on COM1.
  39. *)
  40.  
  41. program PacketMonitor(input,output);
  42.  
  43. uses Dos, Crt;
  44.  
  45. const BufferSize = $3FFF; (* must be 2^n-1 *)
  46.  
  47. type buffer = record
  48.                         ReadPtr, WritePtr:word;
  49.                 Store: array [0..BufferSize] of word
  50.               end;
  51.  
  52. {$S-}{$R-}
  53. procedure InitBuffer(var b:buffer);
  54.   begin
  55.       b.ReadPtr:=0; b.WritePtr:=0
  56.   end;
  57.  
  58. procedure IncBufferPtr(var p:word);
  59.   begin
  60.       inc(p); p:=p and BufferSize
  61.   end;
  62.  
  63. procedure ReadBuffer(var buff:buffer; var w:word; var empty:boolean); assembler;
  64.   asm
  65.     push ds
  66.       les di,empty
  67.       mov dl,0ffh
  68.       mov es:[di],dl
  69.       lds si,buff
  70.       mov ax,[si]; mov bx,si
  71.       mov cx,[si+2]
  72.       cmp ax,cx
  73.       jz @Empt
  74.         mov dl,0; mov es:[di],dl
  75.         les di,w
  76.         add si,4; add si,ax; add si,ax
  77.         mov dx,[si]; mov es:[di],dx
  78.         add ax,1; and ax,BufferSize; mov ds:[bx],ax
  79. @Empt:
  80.     pop ds
  81.   end;
  82.  
  83. (* 'no asm' version of above procedure
  84. procedure ReadBuffer(var b:buffer; var w:word; var empty:boolean);
  85.   begin
  86.     with b do
  87.       begin
  88.         if ReadPtr=WritePtr
  89.           then empty:=true
  90.           else
  91.             begin
  92.               empty:=false;
  93.               w:=Store[ReadPtr];
  94.               IncBufferPtr(ReadPtr)
  95.             end
  96.       end
  97.   end;
  98. *)
  99.  
  100. procedure WriteBuffer(var buff:buffer; w:word; var full:boolean); assembler;
  101.   asm
  102.     push ds
  103.       les di,full
  104.       mov dl,0FFh; mov es:[di],dl
  105.       lds si,buff
  106.       mov ax,[si]; add si,2; mov cx,[si]; mov bx,si; add si,2
  107.       add si,cx; add si,cx
  108.       add cx,1; and cx,BufferSize; cmp ax,cx
  109.       jz @Ful
  110.         mov dl,0; mov es:[di],dl
  111.         mov dx,w; mov [si],dx
  112.         mov [bx],cx
  113. @Ful:
  114.     pop ds
  115.   end;
  116.  
  117. (* 'no asm' version of above routine
  118. procedure WriteBuffer(var b:buffer; w:word; var full:boolean);
  119.   var tmp:word;
  120.   begin
  121.     with b do
  122.       begin
  123.         tmp:=WritePtr; IncBufferPtr(tmp);
  124.         if tmp=ReadPtr
  125.           then full:=true
  126.           else
  127.             begin
  128.               full:=false;
  129.               Store[WritePtr]:=w;
  130.               WritePtr:=tmp
  131.             end
  132.       end
  133.   end;
  134. *)
  135. {$S+}{$R+}
  136.  
  137. procedure EnableInterrupts; inline($FB);
  138.  
  139. procedure DisableInterrupts; inline($FA);
  140.  
  141. const CommBase:word          = $2F8;  (* COM2 I/O base address *)
  142.         IntMask:byte           = $08;   (* IRQ3 mask - bit 3 set *)
  143.         IntNum:byte            = $0B;   (* IRQ3 service routine is INT 0B *)
  144.         TimerBase              = $40;   (* 8253 timer I/O base address *)
  145.  
  146. procedure SelectCOM(com:integer; var ok:boolean);
  147.   begin
  148.     if com=1 then
  149.       begin
  150.         CommBase:=$3f8;
  151.         IntMask:=$10;
  152.         IntNum:=$0C;
  153.         ok:=true;
  154.       end
  155.     else if com=2 then
  156.       begin
  157.         CommBase:=$2f8;
  158.         IntMask:=$08;
  159.         IntNum:=$0B;
  160.         ok:=true
  161.       end
  162.     else writeln('COM',com,' not supported');
  163.   end;
  164.  
  165. Procedure ReadTimer(var time:word); assembler;
  166.   asm
  167.     xor al,al
  168.     out TimerBase+3,al
  169.     in al,TimerBase; xchg al,ah
  170.     in al,TimerBase; xchg al,ah
  171.     les di,time
  172.     mov es:[di],ax
  173.   end;
  174.  
  175. var PrevTime:word;
  176.     LostSamples:word;
  177.  
  178. var PeriodBuffer:Buffer;
  179.  
  180. {$S-}{$R-}
  181. procedure DeltaInterrupt(fl,cs,ip,ax,bx,cx,dx,si,di,ds,es,bp:word); Interrupt;
  182.   var time:word; full:boolean;
  183.   begin
  184.     port[$20]:=$20;
  185.     if (port[CommBase+2] and 7) = 0 then  (* check if modem status interrupt pending *)
  186.       if (port[CommBase+6] and 2) <> 0 then (* check if DSR changed state *)
  187.         begin
  188.           (* ReadTimer(time); *)
  189.       asm
  190.         xor al,al
  191.         out TimerBase+3,al
  192.         in al,TimerBase; xchg al,ah
  193.         in al,TimerBase; xchg al,ah
  194.         mov time,ax
  195.       end;
  196.           WriteBuffer(PeriodBuffer,(PrevTime-time) shr 1,full);
  197.           if full then inc(LostSamples);
  198.           PrevTime:=time
  199.         end
  200.   end;
  201. {$S+}{$R+}
  202.  
  203. procedure InitTimer; (* Is this routine really needed ? *)
  204.   begin
  205. (*
  206.       DisableInterrupts;
  207.       port[TimerBase+3]:=$36;
  208.       port[TimerBase]:=0; port[TimerBase]:=0;
  209.       EnableInterrupts
  210. *)
  211.   end;
  212.  
  213. procedure InitComm; (* Initialize communication port *)
  214.   begin
  215.       DisableInterrupts;
  216.       port[CommBase+3]:=$03;
  217.       port[CommBase+3]:=$83; Port[CommBase]:=$60; port[CommBase+1]:=$00;
  218.       port[CommBase+3]:=$03; (* Base+1 as int. control *)
  219.       port[CommBase+1]:=$00; (* Disable all interrupts *)
  220.       port[CommBase+4]:=$09; (* DTR=high, RTS=low, OUT2=high (?) *)
  221.       EnableInterrupts;
  222.   end;
  223.  
  224. var OldIntVec:pointer;
  225.  
  226. procedure ConnectInterrupt; (* Connect & enable COM interrupt *)
  227.   begin
  228.       ReadTimer(PrevTime); LostSamples:=0;
  229.       DisableInterrupts;
  230.       GetIntVec(IntNum,OldIntVec);
  231.       SetIntVec(IntNum,addr(DeltaInterrupt));
  232.       port[$21]:=port[$21] and (not IntMask); (* Enable IRQ 3/4 in 8259 *)
  233.       port[CommBase+1]:=$08; (* Enable 8250 interrupt on modem status change *)
  234.       EnableInterrupts
  235.   end;
  236.  
  237. procedure DisconnectInterrupt; (* Disable & disconnect COM interrupt *)
  238.   begin
  239.       DisableInterrupts;
  240.       port[CommBase+1]:=$00;           (* Disable all 8250 interrupts *)
  241.       port[$21]:=port[$21] or IntMask; (* Disable IRQ 3/4 in 8259 *)
  242.       SetIntVec(IntNum,OldIntVec);     (* Change INT B/C vector *)
  243.       EnableInterrupts
  244.   end;
  245.  
  246. (* ======================================================================== *)
  247.  
  248. procedure OpenOld(var log:text; name:string);
  249.   begin
  250.     Assign(log,name);
  251.     If FSearch(name,'')=''
  252.       then
  253.         begin
  254.           (* writeln('Creating file ',name); *)
  255.           Rewrite(log)
  256.         end
  257.       else
  258.         begin
  259.           (* writeln('ConLog will be appended to file ',name); *)
  260.           Append(log)
  261.         end
  262.   end;
  263.  
  264. var ConLog:text;
  265.  
  266. procedure OpenConLog(name:string);
  267.   begin
  268.     Assign(ConLog,name);
  269.     If FSearch(name,'')=''
  270.       then
  271.         begin
  272.           (* writeln('Creating file ',name); *)
  273.           Rewrite(ConLog)
  274.         end
  275.       else
  276.         begin
  277.           (* writeln('ConLog will be appended to file ',name); *)
  278.           Append(ConLog)
  279.         end
  280.   end;
  281.  
  282. procedure CloseConLog;
  283.   begin
  284.     close(ConLog)
  285.   end;
  286.  
  287. (* ======================================================================== *)
  288. (* ======================================================================== *)
  289.  
  290. function HexDigit(b:byte):char;
  291.   begin
  292.     if b<10 then HexDigit:=chr(48+b)
  293.     else if b<16 then HexDigit:=chr(65-10+b)
  294.     else HexDigit:=' '
  295.   end;
  296.  
  297. procedure WriteHexByte(var log:text; b:byte);
  298.   begin
  299.     Write(log,HexDigit(b shr 4));
  300.     Write(log,HexDigit(b and $F))
  301.   end;
  302.  
  303. function TwoDigits(w:word):string;
  304.   var tmp:string[2];
  305.   begin
  306.     str(w:2,tmp); if tmp[1]=' ' then tmp[1]:='0';
  307.     TwoDigits:=tmp;
  308.   end;
  309.  
  310. procedure WriteTime(var log:text);
  311.   var h,m,s,ss:word;
  312.   begin
  313.     GetTime(h,m,s,ss);
  314.     write(log,TwoDigits(h),':',TwoDigits(m),':',TwoDigits(s));
  315.   end;
  316.  
  317. procedure WriteDate(var log:text);
  318.   var y,m,d,w:word;
  319.   begin
  320.     GetDate(y,m,d,w);
  321.     write(log,y:4,'-',TwoDigits(m),'-',TwoDigits(d));
  322.   end;
  323.  
  324.  
  325. type ConnPtr = ^ConnRec;
  326.      ConnRec = record
  327.                  sour_dest:string[16];
  328.                  seq:byte;
  329.                  log:text; logname:string[20];
  330.                  next:ConnPtr;
  331.                  activ:integer;
  332.                end;
  333.  
  334. var ConnRoot:ConnPtr;
  335.     LogFileName:string[40];
  336.     LogFileSeq:word;
  337.     OthLogFile:text;
  338.  
  339. function FindConn(sour_dest:string):ConnPtr;
  340.   var ptr:ConnPtr;
  341.   begin
  342.     ptr:=ConnRoot;
  343.     while (ptr<>nil) and (ptr^.sour_dest<>sour_dest) do
  344.       ptr:=ptr^.next;
  345.     FindConn:=ptr
  346.   end;
  347.  
  348. procedure AppendData(sour,dest:string; FrameSeq:byte; data:string);
  349.   var SourDest:string[16]; ptr:ConnPtr; name:string[60];
  350.       dseq:byte;
  351.   begin
  352.     SourDest:=sour+dest;
  353.     ptr:=FindConn(SourDest);
  354.     if ptr=nil
  355.       then
  356.         begin
  357.           new(ptr);
  358.           with ptr^ do
  359.             begin
  360.               next:=ConnRoot; ConnRoot:=ptr;
  361.               str(LogFileSeq,name); name:=LogFileName+'.'+name; inc(LogFileSeq);
  362.               sour_dest:=SourDest;
  363.               writeln('Openning file ',name,' for traffic ',sour,' => ',dest);
  364.               logname:=name; OpenOld(log,logname);
  365.               write(log,'****** File open at '); WriteTime(log);
  366.               write(log,' on '); WriteDate(log);
  367.               writeln(log,' for ',sour,' => ',dest,' traffic');
  368.               seq:=FrameSeq; write(log,data);
  369.               activ:=5
  370.             end
  371.         end
  372.       else
  373.         with ptr^ do
  374.           begin
  375.             dseq:=((16+FrameSeq)-seq) and 7;
  376.             if dseq=1
  377.               then
  378.                 begin
  379.                   write(log,data);
  380.                   seq:=FrameSeq;
  381.                   activ:=5
  382.                 end
  383.               else if (dseq>0) and (dseq<=4) then
  384.                 begin
  385.                   writeln('seq:',seq,'->',FrameSeq,'=>',dseq-1,' frames lost !!!');
  386.                   write(log,' [',dseq-1,' lost pkts] ');
  387.                   write(log,data);
  388.                   seq:=FrameSeq;
  389.                   activ:=5
  390.                 end;
  391.           end
  392.   end;
  393.  
  394. procedure OpenFrameAnalyze(Name:string);
  395.   begin
  396.     ConnRoot:=nil; LogFileSeq:=0; LogFileName:=Name;
  397.     OpenOld(OthLogFile,LogFileName+'.oth');
  398.     Rewrite(OthLogFile);
  399.     write(OthLogFile,'****** File open at '); WriteTime(OthLogFile);
  400.     write(OthLogFile,' on '); WriteDate(OthLogFile);
  401.     writeln(OthLogFile,' for non-categorized data packets');
  402.   end;
  403.  
  404. procedure PrintFrame(var log:text); forward;
  405.  
  406. procedure AnalyzeDataFrame(sour,dest:string; ctrl,pid:byte; data:string);
  407.   var seq:byte;
  408.   begin
  409.     (* writeln(sour,'=>',dest,' seq=',(ctrl shr 1) and 7,' ',length(data),' bytes'); *)
  410.     if (pid=$F0) and ((ctrl and 1) = 0) then
  411.       begin
  412.         seq:=(ctrl shr 1) and 7;
  413.         AppendData(sour,dest,seq,data);
  414.       end
  415.     else if ctrl=$03 then
  416.       (* writeln(data) *) PrintFrame(OthLogFile);
  417.   end;
  418.  
  419. procedure AnalyzeCtrlFrame(sour,dest:string; ctrl:byte);
  420.   begin
  421. (*
  422.     write(sour,'=>',dest);
  423.     if ctrl=$3f then writeln(' connect request')
  424.     else if ctrl=$53 then writeln(' disconnect request')
  425.     else if (ctrl and $F)=1 then writeln(' Rx Ready for seq=',ctrl shr 5)
  426.     else
  427.       begin
  428.         write(' Ctrl:');
  429.         WriteHexByte(output,ctrl);
  430.         writeln
  431.       end
  432. *)
  433.   end;
  434.  
  435. procedure CloseConn(con:ConnPtr);
  436.   begin
  437.     writeln('Closing file ',con^.logname);
  438.     with con^ do
  439.       begin
  440.         writeln(log);
  441.         write(log,'****** File closed at '); WriteTime(log);
  442.         write(log,' on '); WriteDate(log);
  443.         close(log)
  444.       end;
  445.     dispose(con);
  446.   end;
  447.  
  448. procedure CloseFrameAnalyze;
  449.   var ptr,nptr:ConnPtr;
  450.   begin
  451.     ptr:=ConnRoot;
  452.     while ptr<>nil do
  453.       begin
  454.         nptr:=ptr^.next; CloseConn(ptr); ptr:=nptr;
  455.       end;
  456.     ConnRoot:=nil;
  457.     write(OthLogFile,'****** File closed at '); WriteTime(OthLogFile);
  458.     write(OthLogFile,' on '); WriteDate(OthLogFile); Writeln(OthLogFile);
  459.     close(OthLogFile)
  460.   end;
  461.  
  462. procedure CheckActivity;
  463.   var prev:^ConnPtr; con,ncon:ConnPtr;
  464.   begin
  465.     prev:=@ConnRoot; con:=ConnRoot;
  466.     while con<>nil do
  467.       begin
  468.         if con^.activ<=0
  469.           then
  470.             begin
  471.               ncon:=con^.next;
  472.               prev^:=ncon;
  473.               writeln(con^.log);
  474.               writeln(con^.log,'****** connection inactive for 5 minutes');
  475.               CloseConn(con); con:=ncon;
  476.             end
  477.           else
  478.             begin
  479.               writeln('File ',con^.logname,' activ=',con^.activ);
  480.               if con^.activ>0 then dec(con^.activ);
  481.               prev:=@con^.next; con:=con^.next;
  482.             end
  483.       end
  484.   end;
  485.  
  486. (* ======================================================================== *)
  487.  
  488. const MaxFrameLen = 1024;
  489.  
  490. var LogBad,SortTraffic:boolean;
  491.  
  492. var FrameBuff:array [0..MaxFrameLen-1] of byte;
  493.     FramePtr:word; BitCount:word; ByteReg:word;
  494.     ConsBits:word; BadFrame:boolean;
  495.     FrameCount,GoodFrames,CRCErrors:longint;
  496.  
  497. (* The following table & CRC computing routine is taken form PMP package *)
  498.  
  499. const CRCTable:array[0..255] of word = (
  500.         0,  4489,  8978, 12955, 17956, 22445, 25910, 29887,
  501.     35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735,
  502.      4225,   264, 13203,  8730, 22181, 18220, 30135, 25662,
  503.     40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510,
  504.      8450, 12427,   528,  5017, 26406, 30383, 17460, 21949,
  505.     44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797,
  506.     12675,  8202,  4753,   792, 30631, 26158, 21685, 17724,
  507.     48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572,
  508.     16900, 21389, 24854, 28831,  1056,  5545, 10034, 14011,
  509.     52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859,
  510.     21125, 17164, 29079, 24606,  5281,  1320, 14259,  9786,
  511.     57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634,
  512.     25350, 29327, 16404, 20893,  9506, 13483,  1584,  6073,
  513.     61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921,
  514.     29575, 25102, 20629, 16668, 13731,  9258,  5809,  1848,
  515.     65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696,
  516.     33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623,
  517.      2112,  6601, 11090, 15067, 20068, 24557, 28022, 31999,
  518.     38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398,
  519.      6337,  2376, 15315, 10842, 24293, 20332, 32247, 27774,
  520.     42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685,
  521.     10562, 14539,  2640,  7129, 28518, 32495, 19572, 24061,
  522.     46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460,
  523.     14787, 10314,  6865,  2904, 32743, 28270, 23797, 19836,
  524.     50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747,
  525.     19012, 23501, 26966, 30943,  3168,  7657, 12146, 16123,
  526.     54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522,
  527.     23237, 19276, 31191, 26718,  7393,  3432, 16371, 11898,
  528.     59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809,
  529.     27462, 31439, 18516, 23005, 11618, 15595,  3696,  8185,
  530.     63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584,
  531.     31687, 27214, 22741, 18780, 15843, 11370,  7921,  3960 );
  532.  
  533. {$R-}{$S-}
  534. function ComputeCRC:word;
  535.   var p,crc,t:word;
  536.   begin
  537.     crc:=$FFFF;
  538.     for p:=0 to FramePtr-1-2 do
  539.       begin
  540.         t:=FrameBuff[p] xor (crc and $FF);
  541.         crc:=hi(crc) xor CRCTable[t]
  542.       end;
  543.     ComputeCRC:=not crc;
  544.   end;
  545.  
  546. function GetCRC:word;
  547.   begin
  548.     GetCRC:=FrameBuff[FramePtr-2] or (FrameBuff[FramePtr-1] shl 8)
  549.   end;
  550.  
  551. procedure OpenFrame;
  552.   begin
  553.     (* write('=> '); *)
  554.     FramePtr:=0; BitCount:=0; ByteReg:=0; ConsBits:=0; BadFrame:=false
  555.   end;
  556.  
  557. procedure AddBitToFrame(bit:boolean);
  558.  
  559.   procedure AddBit(b:word);
  560.     begin
  561.       ByteReg:=(ByteReg shr 1) or b;
  562.       inc(BitCount);
  563.       if((BitCount and 7) = 0) then
  564.         if FramePtr<MaxFrameLen then
  565.           begin
  566.             FrameBuff[FramePtr]:=lo(ByteReg);
  567.             inc(FramePtr)
  568.           end
  569.         else BadFrame:=true
  570.     end;
  571.  
  572.   begin
  573.     if not BadFrame then
  574.       begin
  575.         (* write(ord(bit):2); *)
  576.         if bit
  577.           then AddBit($80)
  578.           else if ConsBits<5 then AddBit($00);
  579.         if bit
  580.           then inc(ConsBits)
  581.           else ConsBits:=0;
  582.         if ConsBits>5 then
  583.           begin
  584.             (* write('<BS!>'); *)
  585.             BadFrame:=true
  586.           end
  587.       end;
  588.   end;
  589.  
  590. procedure PrintFrameAddress(var log:text; var Ctrl:word);
  591.   var p,l:word;
  592.   begin
  593.     (* write(log,'Addr: '); *)
  594.     p:=0;
  595.     while (p<FramePtr-2) and ((FrameBuff[p] and 1)=0) do inc(p);
  596.     Ctrl:=p+1;
  597.     p:=0;
  598.     while p+7<=Ctrl do
  599.       begin
  600.         for l:=1 to 6 do
  601.           begin
  602.             write(log,chr(FrameBuff[p] shr 1));
  603.             inc(p)
  604.           end;
  605.         write(log,'-',HexDigit((FrameBuff[p] shr 1) and $F));
  606.         if FrameBuff[p]>=$80 then write(log,'R ') else write(log,'  ');
  607.         inc(p)
  608.       end;
  609.     if p<>Ctrl then write(log,'!') else write(log,' ')
  610.   end;
  611.  
  612. procedure PrintFrame(var log:text);
  613.   var b:word; ch:char; ctrl:byte;
  614.   begin
  615.     (* write(ConLog,' [',FramePtr,'] '); *)
  616.     WriteTime(log); write(log,' => ');
  617.     PrintFrameAddress(log,b);
  618.     if b<=FramePtr-1-2 then
  619.       begin
  620.         ctrl:=FrameBuff[b];
  621.               write(log,' Ctrl:'); WriteHexByte(log,ctrl); inc(b);
  622.         if (ctrl and $F)=1 then
  623.                   write(log,' [Rx Ready for seq ',ctrl shr 5,']')
  624.         else if (ctrl and 1) = 0 then
  625.                   write(log,' [Data, seq ',(ctrl shr 1) and 7,']')
  626.         else if ctrl = 3 then
  627.                   write(log,' [UnAck Info]')
  628.         else if ctrl = $3F then
  629.                   write(log,' [Connect Request]')
  630.             end;
  631.     if b<=FramePtr-1-2 then
  632.       begin
  633.               write(log,' Pid:'); WriteHexByte(log,FrameBuff[b]); inc(b)
  634.             end;
  635.     Writeln(log);
  636.     if b<FramePtr-2 then
  637.       begin
  638.         Write(log,' Data: ');
  639.         for b:=b to FramePtr-1-2 do
  640.           begin
  641.             ch:=chr( FrameBuff[b] );
  642.             if (ch>=' ') (* and (ch<chr(127)) *)
  643.               then
  644.                 if ch='#' then write(log,'##')
  645.                           else write(log,ch)
  646.               else
  647.                 begin
  648.                   write(log,'#');
  649.                   WriteHexByte(log,FrameBuff[b])
  650.                 end
  651.           end;
  652.         writeln(log)
  653.       end
  654.   end;
  655.  
  656. procedure GetFrameAddress(var ctrl:word; var sour,dest:string);
  657.   var p:word;
  658.   begin
  659.     for p:=0 to 5 do dest[p+1]:=chr(FrameBuff[p] shr 1);
  660.     dest[7]:='-'; dest[8]:=HexDigit( (FrameBuff[6] shr 1) and $F);
  661.     dest[0]:=#8;
  662.     for p:=7 to 12 do sour[p-6]:=chr(FrameBuff[p] shr 1);
  663.     sour[7]:='-'; sour[8]:=HexDigit( (FrameBuff[13] shr 1) and $F);
  664.     sour[0]:=#8;
  665.     p:=0;
  666.     while (p<FramePtr-2) and ((FrameBuff[p] and 1)=0) do inc(p);
  667.     ctrl:=p+1;
  668.     (* if (ctrl mod 7) <> 0 then write('!!') *)
  669.   end;
  670.  
  671. procedure AnalyzeFrame;
  672.   var b:word; ch:char;
  673.       sour,dest:string[8]; ctrl,pid:byte; data:string[255];
  674.   begin
  675.     GetFrameAddress(b,sour,dest);
  676.     if b<=FramePtr-1-2 then
  677.       begin Ctrl:=FrameBuff[b]; inc(b) end;
  678.     if b<=FramePtr-1-2
  679.       then
  680.         begin
  681.           pid:=FrameBuff[b]; inc(b);
  682.           data:=''; for b:=b to FramePtr-1-2 do data:=data+chr(FrameBuff[b]);
  683.           AnalyzeDataFrame(sour,dest,ctrl,pid,data);
  684.         end
  685.       else
  686.         AnalyzeCtrlFrame(sour,dest,ctrl)
  687.   end;
  688.  
  689. procedure CloseFrame;
  690.   begin
  691.     if FramePtr>=17 then inc(FrameCount)
  692.                     else BadFrame:=true;
  693.     (* if (FramePtr=0) and (BitCount=0) then write('='); *)
  694.     if (BitCount and $7)<>0 then
  695.       begin
  696.         (* write('<BC:',BitCount and 7,'>'); *)
  697. (*
  698.         if LogBad then
  699.           begin
  700.             PrintFrame; Writeln('^^^ Number of bit not a multiple of 8 !!!');
  701.           end;
  702. *)
  703.         BadFrame:=true;
  704.       end;
  705.     if not BadFrame then
  706.       begin
  707.         If ComputeCRC = GetCRC
  708.           then
  709.             begin
  710.               PrintFrame(ConLog);
  711.                             if SortTraffic then AnalyzeFrame;
  712.                             inc(GoodFrames)
  713.             end
  714.           else
  715.             begin
  716.              inc(CRCErrors);
  717.              if LogBad then
  718.                begin
  719.                  PrintFrame(ConLog); writeln(ConLog,'^^^ CRC failed !!!')
  720.                end
  721.             end
  722.       end
  723.     else if (FramePtr>=16) and (FramePtr<=255) then
  724.       begin
  725.         (* write('B!'); PrintFrame *)
  726.       end
  727.   end;
  728.  
  729. const TimerFreq:longint = 1193180;
  730.  
  731. var reg:word; ByteSync:byte;
  732.     PrevBit:boolean;
  733.  
  734. procedure InitAnalyze;
  735.   begin
  736.     reg:=0; ByteSync:=0;
  737.     OpenFrame; BadFrame:=true;
  738.     FrameCount:=0; GoodFrames:=0; CRCErrors:=0;
  739.     PrevBit:=false;
  740.   end;
  741.  
  742. procedure AnalyzeBit(bit:boolean);
  743.   begin
  744.     if Bit xor PrevBit
  745.       then reg:=(reg shl 1)
  746.       else reg:=(reg shl 1) or 1;
  747.     PrevBit:=Bit;
  748.     if ByteSync>0 then dec(ByteSync)
  749.                   else AddBitToFrame( (reg and $100) <> 0 );
  750.     if lo(reg)=$7E then
  751.       begin
  752.         CloseFrame; OpenFrame; ByteSync:=8
  753.         (* write('<F>') *)
  754.       end
  755.   end;
  756.  
  757. (* ======================================================================== *)
  758.  
  759. (* ======================================================================== *)
  760.  
  761. const FilterFIFOLen=63; (* must be 2^n-1 *)
  762.  
  763. var FilterPerFIFO:array [0..FilterFIFOLen] of word;
  764.     FIlterLevFIFO:array [0..FilterFIFOLen] of boolean;
  765.     FilterFIFORdPtr,FilterFIFOWrPtr:word; FilterSum:word;
  766.     FilterSampling:word; FilterSamplingPhase:word;
  767.     FilterTimeLen:word; CorrThreshold:word;
  768.  
  769. var Sample_1,Sample_2:integer;
  770.     Level_1,Level_2:boolean;
  771.     SampleBitNow:boolean;
  772.     SyncStep:word;
  773.  
  774. var SampleAver,InterSampleAver:integer;
  775.  
  776. procedure FilterInit(len,sampling:word);
  777.   begin
  778.     FilterFIFORdPtr:=0;
  779.     FilterPerFIFO[0]:=len; FilterLevFIFO[0]:=false;
  780.     FilterFIFOWrPtr:=1;
  781.     FilterSum:=0;
  782.  
  783.     FilterSampling:=sampling; FilterSamplingPhase:=FilterSampling;
  784.     FilterTimeLen:=len; CorrThreshold:=len shr 1;
  785.  
  786.     Sample_1:=0; Sample_2:=0;
  787.     Level_1:=false; Level_2:=false;
  788.     SampleBitNow:=false;
  789.     SyncStep:=len shr 3;
  790.  
  791.     SampleAver:=0; InterSampleAver:=0;
  792.   end;
  793.  
  794. procedure FilterInput(Level:boolean; Len:word);
  795.   begin
  796.     FilterPerFIFO[FilterFIFOWrPtr]:=Len;
  797.     FilterLevFIFO[FilterFIFOWrPtr]:=Level;
  798.     FilterFIFOWrPtr:=(FilterFIFOWrPtr+1) and FilterFIFOLen;
  799.     if FilterFIFOWrPtr=FilterFIFORdPtr then writeln('Fatal: Filter FIFO overloaded !');
  800.     if Level then inc(FilterSum,Len);
  801.     while Len>0 do
  802.       begin
  803.         if Len<FilterPerFIFO[FilterFIFORdPtr]
  804.           then
  805.             begin
  806.               dec(FilterPerFIFO[FilterFIFORdPtr],Len);
  807.               if FilterLevFIFO[FilterFIFORdPtr] then dec(FilterSum,Len);
  808.               Len:=0;
  809.             end
  810.           else
  811.             begin
  812.               dec(Len,FilterPerFIFO[FilterFIFORdPtr]);
  813.               if FilterLevFIFO[FilterFIFORdPtr] then dec(FilterSum,FilterPerFIFO[FilterFIFORdPtr]);
  814.               FilterFIFORdPtr:=(FilterFIFORdPtr+1) and FilterFIFOLen;
  815.             end
  816.       end
  817.   end;
  818.  
  819. function FilterFIFOuse:word;
  820.   var diff:integer;
  821.   begin
  822.     diff:=FilterFIFOWrPtr-FilterFIFORdPtr;
  823.     if diff>=0
  824.       then FilterFIFOuse:=diff
  825.       else FilterFIFOuse:=FilterFIFOLen+1+diff
  826.   end;
  827.  
  828. const SyncConst=8; SyncConst2=4;
  829.  
  830. procedure FilterNextSample(Signal:word);
  831.   var Sample:integer; Level:boolean; diff,lim:integer;
  832.   begin
  833.     Sample:=Signal-CorrThreshold; Level:=sample>0;
  834.     if SampleBitNow
  835.       then
  836.         begin
  837.           SampleAver:=SampleAver + (10*abs(Sample_1)-SampleAver+16) div 32;
  838.           AnalyzeBit(Level_1);
  839.         end
  840.       else
  841.         begin
  842.           if Level_2 xor Level then
  843.             begin
  844.               diff:=Sample_1; if Level then diff:=-diff;
  845.               InterSampleAver:=InterSampleAver
  846.                             + (10*Sample_1-InterSampleAver+16) div 32 ;
  847.                             if diff>=SyncConst then
  848.                               FilterSamplingPhase:=FilterSamplingPhase+((diff) div SyncConst2)
  849.               else if diff<=-SyncConst then
  850.                               FilterSamplingPhase:=FilterSamplingPhase-((-diff) div SyncConst2)
  851.               else if diff>0
  852.                               then inc(FilterSamplingPhase)
  853.               else if diff<0 then
  854.                               dec(FilterSamplingPhase);
  855.             end;
  856.         end;
  857.     SampleBitNow:=not SampleBitNow;
  858.     Sample_2:=Sample_1; Level_2:=Level_1;
  859.     Sample_1:=Sample;   Level_1:=Level
  860.   end;
  861.  
  862. procedure FilterPreInput(Level:boolean; Len:word);
  863.   begin
  864.     while Len>0 do
  865.       begin
  866.         if Len<FilterSamplingPhase
  867.           then
  868.             begin
  869.               FilterInput(Level,Len);
  870.               dec(FilterSamplingPhase,Len);
  871.               Len:=0;
  872.             end
  873.           else
  874.             begin
  875.               FilterInput(Level,FilterSamplingPhase);
  876.               dec(Len,FilterSamplingPhase);
  877.               FilterSamplingPhase:=FilterSampling;
  878.               FilterNextSample(FilterSum);
  879.             end
  880.       end
  881.   end;
  882.  
  883. (* ======================================================================== *)
  884.  
  885. const ModemFIFOLen=31; (* must be 2^n-1 *)
  886. var ModemFIFO:array [0..ModemFIFOLen] of word;
  887.     ModemFIFORdPtr,ModemFIFOWrPtr:word; ModemFIFOTrans:word;
  888.  
  889. procedure DelayModemInit(delay:word);
  890.   begin
  891.     ModemFIFORdPtr:=0; ModemFIFO[0]:=delay; ModemFIFOWrPtr:=1;
  892.     ModemFIFOTrans:=1;
  893.   end;
  894.  
  895. procedure DelayModemInput(period:word);
  896.   var FirstPer:word;
  897.   begin
  898.     ModemFIFO[ModemFIFOWrPtr]:=period;
  899.     ModemFIFOWrPtr:=(ModemFIFOWrPtr+1) and ModemFIFOLen;
  900.     if ModemFIFOWrPtr=ModemFIFORdPtr then writeln('Fatal: Modem FIFO overloaded !');
  901.     inc(ModemFIFOTrans);
  902.     while period>0 do
  903.       begin
  904.         if period<ModemFIFO[ModemFIFORdPtr]
  905.           then
  906.             begin
  907.               FilterPreInput((ModemFIFOTrans and 1)=0,period);
  908.               dec(ModemFIFO[ModemFIFORdPtr],period); period:=0;
  909.             end
  910.           else
  911.             begin
  912.               FilterPreInput((ModemFIFOTrans and 1)=0,ModemFIFO[ModemFIFORdPtr]);
  913.               dec(period,ModemFIFO[ModemFIFORdPtr]);
  914.               ModemFIFORdPtr:=(ModemFIFORdPtr+1) and ModemFIFOLen;
  915.               dec(ModemFIFOTrans);
  916.             end
  917.       end
  918.   end;
  919.  
  920. function DelayModemFIFOuse:word;
  921.   var diff:integer;
  922.   begin
  923.     diff:=ModemFIFOWrPtr-ModemFIFORdPtr;
  924.     if diff>=0
  925.       then DelayModemFIFOuse:=diff
  926.       else DelayModemFIFOuse:=ModemFIFOLen+1+diff
  927.   end;
  928.  
  929. (* ======================================================================== *)
  930.   const tune:string[19]='                   ';
  931.         ampl:string[10]='          ';
  932.  
  933. procedure DisplayTune;
  934.   var OldX,OldY:byte; freq:word; bin:integer; amp:word;
  935.   begin
  936.     amp:=SampleAver div CorrThreshold;
  937.     if amp>9 then amp:=9;
  938.     bin:=(InterSampleAver div (CorrThreshold div 4));
  939.         if bin>9 then bin:=9 else if bin<-9 then bin:=-9;
  940.     ampl[1+amp]:=chr(48+amp); tune[10-bin]:=chr(48+abs(bin));
  941.     OldX:=WhereX; OldY:=WhereY;
  942.     TextAttr:=TextAttr xor $77;
  943.     GotoXY(42,1); write('A ',ampl,' A');
  944.     GotoXY(58,1); write('T ',tune,' T');
  945.     TextAttr:=TextAttr xor $77;
  946.     GotoXY(OldX,OldY);
  947.     ampl[amp+1]:=' '; tune[10-bin]:=' ';
  948.   end;
  949.  
  950. var period:word; empty,stop :boolean;  key:char;
  951.     com,mode:integer; ok:boolean; delay,width,sampl:word;
  952.     yes_no:char; ConLogName:string[40]; SortedLogName:string[40];
  953.  
  954.     NextMinute,hour,min,sec,hsec:word;
  955.  
  956. begin
  957.   ClrScr;
  958.   writeln('Packet Radio Decoder 1.20 by P.J.');
  959.   writeln;
  960.  
  961.   write('COM 1 or 2 ? '); readln(com);
  962.   SelectCOM(com,ok);
  963.   if not ok then exit;
  964.  
  965.   writeln; writeln('Packet type:');
  966.   writeln('1.  HF packet.  700 Hz center, +/- 100 Hz dev.');
  967.   writeln('2. VHF packet. 1700 Hz center, +/- 400 Hz dev.');
  968.   writeln('3. VHF packet. 1700 Hz center, +/- 500 Hz dev.');
  969.   write('? 1/2/3 '); readln(mode);
  970.   case mode of
  971.     1: begin delay:=400; width:=350; sampl:=600; end;
  972.     2: begin delay:=1360; width:=1133; sampl:=2400; end;
  973.     3: begin delay:=2266; width:=1133; sampl:=2400; end;
  974.   else
  975.     begin
  976.       writeln('Not supported mode'); exit
  977.     end
  978.   end;
  979.  
  980.   writeln; write('Log bad packets ? (y/n) ');
  981.   yes_no:=ReadKey;
  982.   case yes_no of
  983.     'y','Y':begin
  984.               LogBad:=true;
  985.               writeln('will log packets with bad CRC');
  986.             end;
  987.     'n','N':begin
  988.               LogBad:=false;
  989.               writeln('will NOT log bad packets');
  990.             end;
  991.   else
  992.     begin
  993.       writeln(' ... will not log bad packets');
  994.       LogBad:=false;
  995.     end;
  996.   end;
  997.  
  998.   writeln;
  999.   write('File to log all packets [RETURN for console log] ? ');
  1000.     Readln(ConLogName);
  1001.   if ConLogName='' then ConLogName:='con';
  1002.  
  1003.   writeln;
  1004.   writeln('File to log sorted packet traffic');
  1005.     writeln('Give the name only - no extension. Example: c:\log_dir\pktmon');
  1006.   writeln('If you enter empty string sorting will be disabled');
  1007.   write('? '); Readln(SortedLogName);
  1008.   SortTraffic:=not (SortedLogName='');
  1009.  
  1010.   writeln;
  1011.   writeln('Press RETURN to terminate');
  1012.  
  1013.   GetTime(hour,min,sec,hsec);
  1014.   NextMinute:=min+2; if NextMinute>=60 then dec(NextMinute,60);
  1015.  
  1016.   OpenConLog(ConLogName);
  1017.  
  1018.   writeln(ConLog);
  1019.   write(ConLog,'Started Logging on '); WriteDate(ConLog);
  1020.   write(ConLog,' at '); WriteTime(ConLog); writeln(ConLog);
  1021.   if SortTraffic then OpenFrameAnalyze(SortedLogName);
  1022.  
  1023.   InitTimer; InitComm; InitBuffer(PeriodBuffer);
  1024.  
  1025.   DelayModemInit(round(TimerFreq/delay));
  1026.   FilterInit(round(TimerFreq/width),round(TimerFreq/sampl));
  1027.   InitAnalyze;
  1028.  
  1029.   ConnectInterrupt;
  1030.   stop:=false;
  1031.   repeat
  1032.       repeat
  1033.         ReadBuffer(PeriodBuffer,period,empty);
  1034.         if not empty
  1035.             then DelayModemInput(period)
  1036.       until empty;
  1037.     GetTime(hour,min,sec,hsec);
  1038.     if min=NextMinute then
  1039.       begin
  1040.         if SortTraffic then
  1041.                   begin
  1042.             writeln('Checking activity...'); CheckActivity
  1043.           end;
  1044.         NextMinute:=min+1; if NextMinute>=60 then NextMinute:=0;
  1045.       end;
  1046.     (* if mode=1 then *) DisplayTune;
  1047.     if KeyPressed then
  1048.       begin
  1049.         key:=Readkey;
  1050.         case key of
  1051.           #13:stop:=true;
  1052.         end;
  1053.       end;
  1054.   until stop;
  1055.  
  1056.   DisconnectInterrupt;
  1057.   writeln(ConLog,FrameCount,' total frames received and ',GoodFrames,' good ones + ',CRCErrors,' CRC errors');
  1058.   write(ConLog,'Stopped logging on '); WriteDate(ConLog);
  1059.   write(ConLog,' at '); WriteTime(ConLog); writeln(ConLog);
  1060.   if SortTraffic then CloseFrameAnalyze; CloseConLog;
  1061. end.
  1062.